﻿using System;
using System.Collections.Generic;
using System.Linq;
using LibraryHub.Core.Models.Book;
using LibraryHub.Core.Models.LibraryOperation;
using LibraryHub.Core.Models.User;
using LibraryHub.DataAccess.EntityFramework;

namespace LibraryHub.DataAccess
{
    public class BooksDa
    {
        private readonly LibraryHubContext db = DatabaseConnection.Db;

        public IEnumerable<BookModel> GetBooksList()
        {
            var books = (from b in db.Books
                         select b).ToList();
            var result = new List<BookModel>(books.Count);

            foreach (var b in books)
            {
                var tmp = new BookModel
                              {
                                  Id = b.Id,
                                  Title = b.Title,
                                  Description = b.Description,
                                  IsTaken = b.IsTaken
                              };
                result.Add(tmp);
            }

            return result;
        }

        public bool AddBook(BookModel book)
        {
            var efBook = new EfBook { Title = book.Title, Description = book.Description };
            db.Books.Add(efBook); //TODO: No validation at all at the moment. I don't know what is the proper way of enforcing validation.
            int changesCount = db.SaveChanges();

            if (changesCount > 0)
            {
                return true;
            }
            return false;
        }

        public bool EditBook(BookModel book)
        {
            EfBook efBook = db.Books.Find(book.Id);
            efBook.Title = book.Title;
            efBook.Description = book.Description; //TODO: no validation.
            int changesCount = db.SaveChanges();

            return true;
        }

        #region Find

        public BookModel FindBook(int id)
        {
            EfBook book = db.Books.Find(id); //TODO: no validation.
            var result = new BookModel
                             {
                                 Id = book.Id,
                                 Title = book.Title,
                                 Description = book.Description,
                                 IsTaken = book.IsTaken
                             };
            return result;
        }

        public List<BookModel> FindBooksByUserId(int id)
        {
            var dbBooks = from o in db.Operations
                          where !o.OperationClosed
                          where o.EfUserId == id
                          where o.OperationType == (int)LibraryOperationEnum.TakeBook
                          select o.EfBook;

            var result = new List<BookModel>();
            foreach (var dbBook in dbBooks)
            {
                result.Add(new BookModel
                               {
                                   Id = dbBook.Id,
                                   Title = dbBook.Title,
                                   Description = dbBook.Description,
                                   IsTaken = dbBook.IsTaken
                               });
            }

            return result;
        }

        #endregion

        public bool TakeBook(TakeBookModel takeBook)
        {
            var takeBookOperation = new EfLibraryOperation
                                        {
                                            OperationType = (int)LibraryOperationEnum.TakeBook,
                                            OperationValue = takeBook.TakeBookDays,
                                            EfUserId = takeBook.User.Id,
                                            EfBookId = takeBook.Book.Id,
                                            OperationDate = DateTime.Now
                                        };
            // I need transaction with rollback for the case when bad data came into this method
            // and SaveChanges failed.
            db.Operations.Add(takeBookOperation);
            var book = db.Books.Find(takeBook.Book.Id);
            book.IsTaken = true;
            int changesCount = db.SaveChanges();

            if (changesCount > 0)
            {
                return true;
            }
            return false;
        }

        #region Return

        public ReturnBookModel GetReturnBookInfo(int bookId)
        {
            var book = FindBook(bookId);
            if (book == null || !book.IsTaken)
            {
                return null;
            }

            var user = FindBookUser(bookId);
            if (user == null)
            {
                return null;
            }

            var model = new ReturnBookModel {Book = book, User = user};

            return model;
        }

        private UserModel FindBookUser(int bookId)
        {
            var dbUser = FindTakeBookOperation(bookId).EfUser;
            var model = new UserModel
                            {
                                Id = dbUser.Id,
                                Login = dbUser.Login,
                                Password = dbUser.Password,
                                FullName = dbUser.FullName,
                                Role = (UserRoleEnum)dbUser.Role
                            };
            return model;
        }

        public bool ReturnBook(int bookId)
        {
            var takeOperation = FindTakeBookOperation(bookId);
            var returnOperation = new EfLibraryOperation
                                      {
                                          OperationType = (int)LibraryOperationEnum.ReturnBook,
                                          EfUserId = takeOperation.EfUser.Id,
                                          EfBookId = bookId,
                                          OperationDate = DateTime.Now
                                      };
            db.Operations.Add(returnOperation);
            takeOperation.OperationClosed = true;
            var book = db.Books.Find(bookId);
            book.IsTaken = false;
            int changesCount = db.SaveChanges();

            var qwe = db.Operations.Find(takeOperation.Id);

            if (changesCount > 0)
            {
                return true;
            }
            return false;
        }

        #endregion


        public bool DeleteBook(int id)
        {
            EfBook efBook = db.Books.Find(id);
            db.Books.Remove(efBook); //TODO: no validation.
            int changesCount = db.SaveChanges();

            if (changesCount > 0)
            {
                return true;
            }
            return false;
        }

        #region Helpers

        private EfLibraryOperation FindTakeBookOperation(int bookId)
        {
            var operation = (from o in db.Operations
                             where !o.OperationClosed
                             where o.EfBookId == bookId
                             where o.OperationType == (int)LibraryOperationEnum.TakeBook
                             select o).ToList().OrderBy(x => x.OperationDate).Last();

            return operation;
        }

        #endregion

    }
}
